# [八] Bean的实例化-多例作用域

Bean的多例作用域

关于bean的作用域,实际就是bean的管理方式,常见的作用域有五个:

  • singleton(Spring 容器)
  • prototype(Spring 容器)主要讲解多例
  • request(Web 容器)
  • session(Web 容器)
  • application(Web 容器)

Spring 容器加载的时候,大部分处理的是单例bean的实例化过程,也就是默认的bean作用域,而且Spring 容器对多例bean默认采取的是懒加载,所以多例bean并不会实例化,那么当BeanDefinition中bean的属性为多例的时候,Spring 是如何处理的呢?

# 多例Bean的代码

定义一个多例模式的类

如果SCOPESCOPE_PROTOTYPE时,不管是不是同一个线程,只要是 getBean就会得到一个新 的实例

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBean {

}

# 多例Bean的流程

多例Bean的调用流程和单例Bean基本一致,只是没有放到缓存中,因此循环依赖会报错。

进入 doGetBean()方法,该方法是多例Bean的入口

类文件: org.springframework.beans.factory.support.AbstractBeanFactory

else {

			/**
			 * TOTO : 处理原型模式下循环依赖的问题
			 * --触发场景:原型模式下,如果存在A中有B属性,B中有A属性,那么当依赖注入的时候,
			 * 就会产生当A还没创建完的时候,由于对B的创建再次返回创建A,造成循环依赖
			 *
			 * 如果 singletonObjects 缓存里面没有,则走下来
			 * 如果是 scope 是 Prototype 的,校验是否有出现循环依赖,
			 * 如果ThreadLocal有,则直接报错
			 */
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}
			// 检查一下这个 BeanDefinition 在容器中是否存在
			BeanFactory parentBeanFactory = getParentBeanFactory();
			// 如果不存在 beanDefinitionMap (一个concurrenthashmap)容器中,也就是所有已经加载的类中不包括
			// beanName,则尝试从 parentBeanFactory 中检测
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// 如果当前容器不存在这个 BeanDefinition,试试父容器中有没有
				String nameToLookup = originalBeanName(name);
				if (parentBeanFactory instanceof AbstractBeanFactory) {
					return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
							nameToLookup, requiredType, args, typeCheckOnly);
				}
				else if (args != null) {
					// 使用显式参数委托给父进程。
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else if (requiredType != null) {
					// 没有 args -> 委托给标准的 getBean 方法
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
				else {
					return (T) parentBeanFactory.getBean(nameToLookup);
				}
			}

继续执行下面多例逻辑的代码

类文件: org.springframework.beans.factory.support.AbstractBeanFactory

// 如果是 prototype scope 的,创建 prototype 的实例
else if (mbd.isPrototype()) {
	Object prototypeInstance = null;
    try {
// 多例bean底层是通过 ThreadLocal<Object> 来存放的,创建之前先看		     //ThreadLocal<Object>是否存在,不存在则添加bean到ThreadLocal<Object> 中。
// 控制循环依赖,
        beforePrototypeCreation(beanName);
        // 直接返回创建对象,不会放到缓存中
        prototypeInstance = createBean(beanName, mbd, args);
    }
    finally {
        afterPrototypeCreation(beanName);
    }
    // 该方法是 FactoryBean 接口的调用入口
    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
    }

进入createBean()方法, org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
	// //主要看这个方法,重要程度 5		
	Object beanInstance = doCreateBean(beanName, mbdToUse, args);
 }

进入 doCreateBean()方法, 类文件:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

# 其他作用域解读

doGetBean()方法,其他实现类来实现的作用域的入口

类文件: org.springframework.beans.factory.support.AbstractBeanFactory

// 如果不是 singleton 和 prototype 的话,需要委托给相应的实现类来处理
else {
	String scopeName = mbd.getScope();
	final Scope scope = this.scopes.get(scopeName);
	if (scope == null) {
		throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
	}
	try {
		Object scopedInstance = scope.get(beanName, () -> {
			beforePrototypeCreation(beanName);
			try {
				// 执行创建 Bean
				return createBean(beanName, mbd, args);
			}
			finally {
				afterPrototypeCreation(beanName);
			}
		});
		// 该方法是FactoryBean接口的调用入口
		bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
	}
	catch (IllegalStateException ex) {
		throw new BeanCreationException(beanName,
				"Scope '" + scopeName + "' is not active for the current thread; consider " +
				"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
				ex);
		}
	}
}

# 自定义作用域

1、定义一个类继承 BeanFactoryPostProcessor接口,拿到ConfigurableListableBeanFactory类的引用,

@Component
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    	// 设置自定义的scope 名字,和解析类
        beanFactory.registerScope("jackScope",new CustomScope());
    }
}

2、定义解析类

public class CustomScope implements Scope {
    // 通过 ThreadLocal 来管理bean
    private ThreadLocal local = new ThreadLocal();
    /*
    * 这个方法就是自己管理bean
    * */
    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        if(local.get() != null) {
            return local.get();
        } else {
            //这个方法就是掉createbean方法获得一个实例
            Object object = objectFactory.getObject();
            local.set(object);
            return object;
        }
    }
  
}

3、定义入口类

@Component
@Scope("jackScope")
public class CustomScopeBean {
    private String username;
    public String getUsername() {
        return this.username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
}

4、测试类

 @Test
    public void customScopeTest() {
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            new Thread(() -> {
                if(finalI % 2 == 0) {
                    System.out.println(Thread.currentThread().getName() + "-->" + applicationContext.getBean("customScopeBean"));
                    System.out.println(Thread.currentThread().getName() + "-->" + applicationContext.getBean("customScopeBean"));
                }
                else {
                    System.out.println(Thread.currentThread().getName() + "-->" + applicationContext.getBean("customScopeBean"));
                }

            }).start();
        }
    }

5、执行结果,可见同一个线程执行两次的结果hash一致

Thread-5-->com.xiangxue.jack.scope.CustomScopeBean@132d6fa
Thread-1-->com.xiangxue.jack.scope.CustomScopeBean@b4a3ea2
Thread-8-->com.xiangxue.jack.scope.CustomScopeBean@191edca8
Thread-6-->com.xiangxue.jack.scope.CustomScopeBean@32280ef5
Thread-7-->com.xiangxue.jack.scope.CustomScopeBean@7fbdeb7d
Thread-3-->com.xiangxue.jack.scope.CustomScopeBean@1601fd2a
Thread-9-->com.xiangxue.jack.scope.CustomScopeBean@399e5f47
Thread-10-->com.xiangxue.jack.scope.CustomScopeBean@283b28be
Thread-3-->com.xiangxue.jack.scope.CustomScopeBean@1601fd2a
Thread-7-->com.xiangxue.jack.scope.CustomScopeBean@7fbdeb7d
Thread-1-->com.xiangxue.jack.scope.CustomScopeBean@b4a3ea2
Thread-4-->com.xiangxue.jack.scope.CustomScopeBean@44d305aa
Thread-5-->com.xiangxue.jack.scope.CustomScopeBean@132d6fa
Thread-2-->com.xiangxue.jack.scope.CustomScopeBean@30fd9718
Thread-9-->com.xiangxue.jack.scope.CustomScopeBean@399e5f47